package ch.usi.inf.pf2.cell;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
 * @author Yotam Sharon, Thomas Mantegazzi
 * 
 *         Convert a cell reference to indexes ("A1" -> int[]{1,1}) or ("AB23"
 *         -> int[]{27,23}) and vice versa
 */
public class Converter {

	private String cellReference;
	private int[] cellReferenceNumbers;
	private HashMap<Character, Integer> letters;
	private HashMap<Integer, Character> numbers;

	/**
	 * Construct a Converter object to convert from String to int[] and vice
	 * versa
	 */
	public Converter() {
		cellReference = null;
		cellReferenceNumbers = null;
		letters = new HashMap<Character, Integer>();
		numbers = new HashMap<Integer, Character>();
		fillLetters();
		fillNumbers();
	}

	/**
	 * Return the cellReference as a String
	 * 
	 * @return cellReference
	 */
	public String getCellReference() {
		return cellReference;
	}

	/**
	 * Return the cellReferenceNumbers as an int[]
	 * 
	 * @return cellReferenceNumbers
	 */
	public int[] getCellReferenceNumbers() {
		return cellReferenceNumbers;
	}

	/**
	 * Return this letters
	 * 
	 * @return letters
	 */
	public HashMap<Character, Integer> getLetters() {
		return letters;
	}

	/**
	 * Return this numbers
	 * 
	 * @return numbers
	 */
	public HashMap<Integer, Character> getNumbers() {
		return numbers;
	}

	/**
	 * Change the cellReference to convert
	 * 
	 * @param cellReference
	 */
	public void setCellReference(String cellReference) {
		this.cellReference = cellReference;
	}

	/**
	 * Change the cellReferenceNumbers to convert
	 * 
	 * @param cellReferenceNumbers
	 */
	public void setCellReferenceNumbers(int[] cellReferenceNumbers) {
		this.cellReferenceNumbers = cellReferenceNumbers;
	}

	/**
	 * Fill the HashMap letters, with the letters A-Z corresponding to 1-26
	 */
	private void fillLetters() {
		letters.put('A', 1);
		letters.put('B', 2);
		letters.put('C', 3);
		letters.put('D', 4);
		letters.put('E', 5);
		letters.put('F', 6);
		letters.put('G', 7);
		letters.put('H', 8);
		letters.put('I', 9);
		letters.put('J', 10);
		letters.put('K', 11);
		letters.put('L', 12);
		letters.put('M', 13);
		letters.put('N', 14);
		letters.put('O', 15);
		letters.put('P', 16);
		letters.put('Q', 17);
		letters.put('R', 18);
		letters.put('S', 19);
		letters.put('T', 20);
		letters.put('U', 21);
		letters.put('V', 22);
		letters.put('W', 23);
		letters.put('X', 24);
		letters.put('Y', 25);
		letters.put('Z', 26);
	}

	/**
	 * Fill the HashMap numbers, with the numbers 1-26 corresponding to A-Z
	 */
	public void fillNumbers() {
		numbers.put(1, 'A');
		numbers.put(2, 'B');
		numbers.put(3, 'C');
		numbers.put(4, 'D');
		numbers.put(5, 'E');
		numbers.put(6, 'F');
		numbers.put(7, 'G');
		numbers.put(8, 'H');
		numbers.put(9, 'I');
		numbers.put(10, 'J');
		numbers.put(11, 'K');
		numbers.put(12, 'L');
		numbers.put(13, 'M');
		numbers.put(14, 'N');
		numbers.put(15, 'O');
		numbers.put(16, 'P');
		numbers.put(17, 'Q');
		numbers.put(18, 'R');
		numbers.put(19, 'S');
		numbers.put(20, 'T');
		numbers.put(21, 'U');
		numbers.put(22, 'V');
		numbers.put(23, 'W');
		numbers.put(24, 'X');
		numbers.put(25, 'Y');
		numbers.put(26, 'Z');
	}

	/**
	 * Return the part of the cell reference composed by letters (if input "A1",
	 * return "A"). If an error occurred, return null.
	 * 
	 * @return cellLetters The letters part of cellReference
	 */
	public String getCellLetters() {
		Pattern pattern = Pattern.compile("[A-Z]+");
		Matcher matcher = pattern.matcher(cellReference);
		if (matcher.find()) {
			return matcher.group();
		} else {
			return null;
		}
	}

	/**
	 * Return the part of the cell reference composed by numbers (if input "A1",
	 * return 1) If an error occurred, return -1.
	 * 
	 * @return cellNumbers
	 */
	public int getCellNumbers() {
		Matcher matcher = Pattern.compile("[0-9]+").matcher(cellReference);
		if (matcher.find()) {
			return Integer.parseInt(matcher.group());
		} else {
			return -1;
		}
	}

	/**
	 * Converts the cellReference of this Converter into an int[]. Store the
	 * result in the field cellReferenceNumbers
	 */
	public void convertToNumbers() {
		if (cellReference == null) {
			return;
		}
		String cellLetters = getCellLetters();
		int numbers = getCellNumbers();
		if (numbers == -1 || cellLetters == null) {
			return;
		}
		// converts
		char[] chars = new char[cellLetters.length()];
		cellLetters.getChars(0, cellLetters.length(), chars, 0);
		// compute number
		int sum = 0;
		int index = 0;
		for (int i = chars.length - 1; i >= 0; i--) {
			sum += letters.get(chars[i]) * Math.pow(26, index);
			index++;
		}
		cellReferenceNumbers = new int[] { sum, numbers };
	}

	/**
	 * Converts the cellReferenceNumbers of this Converter into a String. Store
	 * the result in the field cellReference
	 */
	public void convertToString() {
		if (cellReferenceNumbers == null) {
			return;
		}
		int number = cellReferenceNumbers[0];
		int remainder = 0;
		String convertedString = "";
		while (true) {
			if (number % 26 == 0) {
				number = number / 26 - 1;
				convertedString = numbers.get(26).toString() + convertedString;
			} else {
				remainder = number % 26;
				number = number / 26;
				convertedString = numbers.get(remainder).toString()
						+ convertedString;
			}
			if (number <= 26) {
				if (number < 1) {
					break;
				}
				convertedString = numbers.get(number).toString()
						+ convertedString;
				break;
			}
		}
		cellReference = convertedString + cellReferenceNumbers[1];
	}

}
